home *** CD-ROM | disk | FTP | other *** search
/ MIDICraft's MIDINET CD-ROM / MIDICraft's MIDINET CD-ROM.iso / DOCS / MIDISMF2.ZIP / MIDISMF2.TXT
Encoding:
Text File  |  1990-11-13  |  25.4 KB  |  836 lines

  1. Title:          Standard MIDI Files - Part II... Coding Hints
  2.  
  3.  
  4. Last month I went through the current SMF standard looking at the overall 
  5. layout of the 'chunks', and the events which can be placed inside track 
  6. chunks. This month it's time to look deeper, ie tackle some of the routines
  7. which are used by SMF programs.
  8.  
  9. There's several questions that need to be answered...
  10.  
  11.    1: How do we access the file in the first place. Do we load the 
  12.       file into our own buffer, or open it for direct reading and 
  13.       let the system handle the buffer arrangements ?
  14.  
  15.    2: How do we find out what type of SMF files we are dealing with ?
  16.  
  17.       
  18.    3: How do we read through the events present ?
  19.       
  20.  
  21.  
  22. File access methods depend very much on what you intend doing. If you 
  23. going to read and interpret the data as a stream of bytes then C's 
  24. standard file handling routines will be fine. If you have ideas about 
  25. scanning a multi-chunk type 1 file and building pointer tables so the 
  26. events of many tracks can be 'played back' according to their global time 
  27. ordering, then you'll be needing fast random access to events and it's then
  28. best to have the file under your control in memory.
  29.  
  30. Loading the complete file into memory SHOULD bring the C programmer a 
  31. another advantage - namely that structure definitions could be used for 
  32. chunks, chunk identifiers, headers etc., (much the same as the Amiga's IFF
  33. standard operates). Here's one of my headers that shows the sort of 
  34. definitions that could be set up...
  35.  
  36.  
  37. /* ----------------------------------------------------------------------- */
  38. /* Title:        Standard MIDI File General Header                         */
  39. /* Disk Ref:     smf.h                                                     */
  40. /* ----------------------------------------------------------------------- */
  41.  
  42. /* Four character IDentifier builder */
  43. #define MakeID(a,b,c,d)  ( (LONG) (a)<<24L | (LONG) (b)<<16L | (c)<<8 | (d) )
  44.  
  45. /* Standard MIDI File IDs. SMF files ALWAYS start with an ID_HEADER chunk 
  46. which are then followed by one or more ID_TRACK chunks. New chunk types may
  47. be added... so chunk readers must be prepared to skip over chunks any which
  48. they do not understand or need */
  49.  
  50. #define  ID_HEADER MakeID('M','T','h','d')
  51. #define  ID_TRACK MakeID('M','T','r','k')
  52.  
  53. /* ----------------------------------------------------------------------- */
  54. /* Chunks start with a type ID and a count of the data bytes that follow */
  55.  
  56. typedef struct {
  57.       LONG  ckID;
  58.       LONG  ckSize;
  59.       }ChunkInfo;
  60.  
  61. typedef struct {
  62.       LONG  ckID;
  63.       LONG  ckSize;
  64.       UBYTE ckData[ 1 /* don't forget that this is really ckSize */ ];
  65.       }Chunk;
  66.  
  67.  
  68. typedef struct {
  69.       LONG  ckID;
  70.       LONG  ckSize;
  71.       UWORD Format;
  72.       UWORD Tracks;
  73.       UWORD Division;
  74.       }HeaderChunk;
  75.  
  76.  
  77. /* ChunkPSize computes total physical size of a chunk from it's data size */
  78.  
  79. #define ChunkPSize(dataSize)  (dataSize + sizeof(ChunkInfo))
  80.  
  81.  
  82. /* ----------------------------------------------------------------------- */
  83.  
  84.  
  85. This structure orientated approach is fine and leads to nice clean code 
  86. but to make maximum use of it you have to be prepared to do some work as 
  87. you read the file into memory. What work ? Track chunks have to be padded
  88. so that they become an even number of bytes long - if you don't the poor 
  89. old 68000 chip will go cranky as it starts seeing odd addresses during 
  90. structure-orientated chunk access. 
  91.  
  92. I'm not going to deal with padded-read techniques because, to be honest, 
  93. it's not the way most people handle SMF files. I mention it in passing 
  94. because it just seems a shame that the SMF standard didn't specify pad 
  95. bytes for odd byte length chunks ! 
  96.  
  97. If you arrange for your buffer to start on an even boundary then it is
  98. possible to access the header chunk via the structure orientated definitions 
  99. given in the smf.h header file. At the moment, since the header chunk is an 
  100. even number of bytes long you could handle a first track chunk in this way
  101. as well (so all type 0 files could be safely handled with this approach).
  102.  
  103. For general multi-chunk analysis however any pointer is likely to need to 
  104. move through the buffer on a byte-by-byte basis so one generally useful
  105. solution is to declare the buffer pointer as say...
  106.  
  107.           UBYTE *pointer;
  108.  
  109. and then use casts for the structure-allowable fields, like this...
  110.  
  111.           identity=((HeaderChunk *)pointer)->ckID; /* Get Chunk identity */
  112.  
  113.           /* first check that the file loaded is actually a MIDI File */
  114.  
  115.           if(identity!=ID_HEADER) {printf(ERROR_MESSAGE_2);}
  116.  
  117.           else { do something with this MIDI file }
  118.  
  119.  
  120. The format and track count fields can be obtained in a similar fashion,
  121. for example...
  122.  
  123.           format=((HeaderChunk *)pointer)->Format;
  124.  
  125.           tracks=((HeaderChunk *)pointer)->Tracks;
  126.  
  127.           printf("%s %d %s %d ", "This is a ",
  128.           
  129.           tracks," track SMF type ",format);
  130.  
  131.  
  132. We can use this sort of info to build a high-level chunk reading loop like 
  133. this..
  134.  
  135.  
  136.           for (i=0;i<tracks;i++)
  137.  
  138.                       { 
  139.  
  140.                        Do some preliminary setting up of any pointers
  141.                        which are to be used
  142.  
  143.                        Analyse Track Chunk
  144.    
  145.                        } /* end of for-loop */
  146.  
  147.  
  148. In order to analyse a track chunk you've got to be able to skip through
  149. the various events. This cannot be done do without interpreting those 
  150. variable length items, such as the delta-times and size values for sysex
  151. events, so we need to have a look at this in more detail...
  152.  
  153.  
  154.  
  155. Delta-Times and Variable Length Numbers 
  156. ---------------------------------------
  157.  
  158. We saw last month that the track chunks of standard MIDI files store 
  159. information about MIDI events and other related data. Each event in 
  160. the  file is preceded by a  'delta-time'  which represents the number 
  161. of clocks which should elapse between the previous event and the 
  162. start of a new event.
  163.  
  164. Delta-times, just to remind you, are stored using 7 bits (bits 0 - 6) 
  165. per byte with the most significant bits coming first.  Bit 7  itself is 
  166. used only as a position indicator - all bytes except the last byte have 
  167. bit 7 set high, the last byte has bit 7 set low. 
  168.  
  169. The format used for delta-time representation is the same as that 
  170. used for several other SMF fields, so the following decoding routines 
  171. therefore turn out to be of general use. To decode a variable length
  172. number you look at the first byte of the number (which may of course 
  173. also be the last byte) and extract the lower seven bits (bit 0 -> bit 6
  174. inclusive) of the byte. If bit 7 of the byte was set then you'll know 
  175. that  the next byte is also part of the number....  so you  shift the 
  176. first seven bits along, and bring in the next seven bits from the next 
  177. byte. You repeat this process until the byte being dealt with has bit 7 
  178. clear,  i.e.  until you're dealing with the last byte of the number.
  179.  
  180. I'll tackle the code problems from the point of view of routines which 
  181. read from a buffer set up by the program itself, namely because that's
  182. what our example program does. 
  183.  
  184. To give you an example of the type of code needed we'll assume 
  185. that you have loaded a Standard MIDI File into a buffer and that 
  186. buffer_p is a pointer which points to the start of the buffer, or 
  187. the start of one of the delta-times present in the buffer. First 
  188. here's some C code WITHOUT any of the usual C shortcuts, so you 
  189. can see exactly what needs to be done...
  190.  
  191.  
  192. time = *buffer_p;          /* get a byte */
  193.  
  194. buffer_p = buffer_p+1;     /* increment the pointer */
  195.  
  196. if(time & 0x80)            /* if bit 7 is set */
  197.  
  198.    {
  199.  
  200.    time=time &0x7F;      /* mask out bit 7 */
  201.  
  202.    do
  203.  
  204.       {
  205.  
  206.       nextbyte = *buffer_p;    /* get next byte */
  207.  
  208.       buffer_p = buffer_p+1;   /* increment pointer */
  209.  
  210.       time = (time <<7) + (nextbyte & 0x7F); /* shift data and add new bits */
  211.  
  212.       }while (nextbyte & 0x80);
  213.  
  214.    }
  215.  
  216. /* time variable now holds the decoded delta-time */
  217.  
  218.  
  219. If  we  shorten  this code by using a few standard  C  tricks  to 
  220. combine the reading,  incrementing and testing operations, we'll end 
  221. up with code which looks like this...
  222.  
  223.  
  224. if (( time = *buffer_p++) & 0x80)
  225.  
  226.     {
  227.  
  228.      time &= 0x7F;
  229.  
  230.      do
  231.  
  232.         {
  233.  
  234.          nextbyte = *buffer_p++; 
  235.  
  236.          time = (time<<7) + (nextbyte & 0x7F);
  237.  
  238.          } while (nextbyte & 0x80);
  239.  
  240.     }
  241.  
  242.      /*  The variable time 'time' now contains the 
  243.          decoded delta-time in ULONG integer form */
  244.  
  245.  
  246.  
  247. With 'time' and 'nextbyte' declared as ULONG (unsigned long ints) variables 
  248. such procedures  can  only handle integers up to four bytes in size. The 
  249. early SMF specs didn't specify an upper limit and, in theory at least, 
  250. it was possible to have delta-times which contained any number of bytes. 
  251. Nowadays the problem has disappeared - the standard specifies that a 
  252. maximum of four bytes only may be used to specify a delta-time !
  253.  
  254. If you've forgotten why this variable length arrangement is used in 
  255. Standard MIDI Files here's the reason again... it's because most MIDI 
  256. events come close together, i.e. the delta-times between events are 
  257. small values (often numerically less than 128). By using a variable 
  258. length format it's possible to store most delta-times using only one 
  259. byte, yet when larger values are required more bytes can be used. If a 
  260. fixed length scheme had been  adopted the byte length would have had to 
  261. be 4 bytes for each delta-time in order to cope with the occasional high 
  262. value delta-time that might be required (2 bytes would not have been 
  263. sufficient and 3 bytes would have made life rather awkward for the 
  264. programmer). 
  265.  
  266. The  above example is similar to the code originally suggested by 
  267. Opcode  systems and it is optimized for the analysis of single byte 
  268. delta-times. Using the standard resolution the delta-times are mainly 
  269. single byte values... but as the 'ticks-per-crotchet' resolution 
  270. increases, numerical delta-time values increase as well leading to 
  271. increased delta-time overall byte sizes. Standard MIDI Files allow for 
  272. the storage of data using higher  resolutions and it's possible that a 
  273. slightly re-arranged decoding  routine  would be more appropriate under  
  274. higher tick resolution conditions. Here's a sketch of an alternative 
  275. read routine  which should be more efficient for those files where the 
  276. delta-times consist mainly of two or more bytes...
  277.  
  278.  
  279.      do 
  280.           {  nextbyte = buffer_p++; 
  281.  
  282.              time = ( time<<7 ) + ( nextbyte & 0x7F );
  283.     
  284.            }while(!(nextbyte &0x80));
  285.  
  286.  
  287.  
  288. Which method have I used ? It's a pointer-based variation on the first 
  289. method we looked at. Here's the complete function code as you'll find it
  290. in the example program...
  291.  
  292.  
  293. ULONG ReadVarLen(void)
  294.  
  295. {
  296.  
  297. ULONG value; UBYTE c;
  298.  
  299. value=(ULONG)*local_pointer++;
  300.  
  301. if(value&0x80)
  302.  
  303.    { 
  304.    
  305.    value&=0x7F;
  306.    
  307.    do {value=(value<<7)+((c=*local_pointer++)&0x7F);} while(c&0x80);
  308.  
  309.    }
  310.  
  311. return(value);
  312.  
  313. }
  314.  
  315. You wil also see a similar routine ReadChunkSize() which gets around the 
  316. problem of reading any chunk's chunksize value without using a structure 
  317. reference...
  318.  
  319. ULONG ReadChunkSize(UBYTE *RCS_pointer)
  320.  
  321. {
  322.  
  323. ULONG value; UBYTE i;
  324.  
  325. RCS_pointer+=4; /* skip over chunk identifier */
  326.  
  327. value=*RCS_pointer++;
  328.  
  329. for (i=0;i<3;i++) { value=(value<<8)+(*RCS_pointer++);}
  330.  
  331. return(value);
  332.  
  333. }
  334.  
  335.  
  336. Another almost identical routine can be used to read the chunk identities 
  337. of non word-aligned chunks buried inside the file...
  338.  
  339. ULONG ReadChunkID(UBYTE *RCID_pointer)
  340.  
  341. {
  342.  
  343. ULONG value; UBYTE i;
  344.  
  345. value=*RCID_pointer++;
  346.  
  347. for (i=0;i<3;i++) { value=(value<<8)+(*RCID_pointer++);}
  348.  
  349. return(value);
  350.  
  351. }
  352.  
  353.  
  354. A Runable Example
  355. -----------------
  356.  
  357. The following example is only a skeleton command-line SMF reader. If you
  358. give it a path/filename which corresponds to an SMF file it will load the 
  359. file and look at the header to see what type of SMF file it is, and then 
  360. loop through each chunk counting the various events it comes across. I've 
  361. opted for quite a large set of global variables - I'm aware of the 
  362. disadvantages of globals but in this particular case it really does help 
  363. avoid a lot of time consuming parameter passing. 
  364.  
  365. As well as a high level chunk pointer there's a duplicate local pointer 
  366. set up - as events are recognized they are counted, and then the local 
  367. pointer is incremented by a value which results in skipping to the next 
  368. event (the sizes of Meta-events and Sysex events are calculated, MIDI-event 
  369. sizes are predefined). 
  370.  
  371. The program handles all event types, can cope with running status and, on 
  372. most occasions will even recover from a badly formed event or bad chunk. 
  373. Once the header chunk has been read it should only find track chunks but,
  374. if it does suddenly come across an unknown ID it will skip that chunk and
  375. continue looking for the track chunks that the header said were supposed
  376. to be there. At the end of the day, if the file is OK, the program prints 
  377. a list giving the numbers of events it encountered. 
  378.  
  379.  
  380.  
  381. /* ======================================================================= */
  382. /* Standard MIDI File - Example High Level Reader Code                     */
  383. /* Paul Overaa - September 90                                              */
  384. /* ----------------------------------------------------------------------- 
  385.  
  386. Here's the basic idea behind the high level code...
  387.  
  388. Specify include files, prototypes, defines, and globals to be used.
  389.  
  390. First run-time job is to load the user specified file into the buffer.
  391.  
  392.     If load NOT OK  { give an error message }
  393.               
  394.               else  { look to see whether it really is a MIDI file
  395.               
  396.                       If NOT a MIDI File { give an error message }
  397.               
  398.                            else {  
  399.                                 
  400.                                    1: Get the header details
  401.                                 
  402.                                    2: Loop analysing each chunk in turn
  403.       
  404.                                    3: Display some results
  405.                                  
  406.                                  }
  407.                     }
  408.   ------------------------------------------------------------------------ */
  409.  
  410. /* includes */
  411.  
  412. #include <stdio.h>
  413. #include <types.h>
  414. #include <ram:smf.h>
  415.  
  416. /* prototypes */
  417.  
  418. BOOL  LoadBuffer(TEXT *filename, UBYTE *buffer_p, LONG buffersize);
  419. ULONG ReadChunkSize(UBYTE *RCS_pointer);
  420. ULONG ReadChunkID(UBYTE *RCID_pointer);
  421. ULONG ReadVarLen(void);
  422. void  SysexEvent(void);
  423. void  MidiEvent(void);
  424. void  MetaEvent(void);
  425. void  AnalyseTrackChunk(void);
  426.  
  427. /* defines */
  428.  
  429. #define BUFFERSIZE  10000
  430. #define READ        "r"
  431. #define NOTE_OFF                0x80
  432. #define NOTE_OFF_SIZE              2
  433. #define NOTE_ON                 0x90
  434. #define NOTE_ON_SIZE               2
  435. #define PAT                     0xA0
  436. #define PAT_SIZE                   2
  437. #define CONTROL_CHANGE          0xB0
  438. #define CONTROL_CHANGE_SIZE        2
  439. #define PROGRAM_CHANGE          0xC0
  440. #define PROGRAM_CHANGE_SIZE        1
  441. #define CHANNEL_PRESSURE        0xD0
  442. #define CHANNEL_PRESSURE_SIZE      1
  443. #define PITCHBEND               0xE0
  444. #define PITCHBEND_SIZE             2
  445.  
  446. /* some messages */
  447.  
  448. #define NO_ERROR_MESSAGE "file which looks OK, here are some details:\n\n"
  449. #define ERROR_MESSAGE_1  "Cannot load this file\n"
  450. #define ERROR_MESSAGE_2  "This is NOT a Standard MIDI File\n"
  451. #define ERROR_MESSAGE_3  "file but it doesn't look right\n"
  452. #define ERROR_MESSAGE_4  "We've hit (and skipped over) an UNKNOWN CHUNK\n"
  453.  
  454. /* some globals */
  455.  
  456. BOOL    global_exit_flag, g_unrecognized_byte=FALSE;
  457.  
  458. UBYTE   buffer[BUFFERSIZE], g_current_status, *g_pointer, *g_local_pointer; 
  459.  
  460. ULONG   g_current_datasize, g_previous_datasize,
  461.         g_meta_event_count=0, g_sysex_event_count=0,
  462.         g_note_off_event_count=0, g_note_on_event_count=0, 
  463.         g_pat_event_count=0, g_control_change_event_count=0, 
  464.         g_program_change_event_count=0, g_channel_pressure_event_count=0, 
  465.         g_pitchbend_event_count=0;
  466. /* ----------------------------------------------------------------------- */
  467.  
  468. main(int argc, char *argv[])
  469.  
  470. {
  471.  
  472. ULONG identity; 
  473.  
  474. UWORD format, tracks; 
  475.  
  476. UBYTE i;
  477.  
  478. if (LoadBuffer(argv[1], buffer, BUFFERSIZE)==TRUE)
  479.  
  480.          {printf(ERROR_MESSAGE_1);}
  481.  
  482.     else { 
  483.  
  484.           g_pointer=buffer;          
  485.  
  486.           identity=((HeaderChunk *)g_pointer)->ckID; /* Get Chunk identity */
  487.  
  488.           /* first check that the file loaded is actually a MIDI File */
  489.  
  490.           if(identity!=ID_HEADER) {printf(ERROR_MESSAGE_2);}
  491.  
  492.            else { /* This is a Standard MIDI File, so get header details */
  493.  
  494.                  format=((HeaderChunk *)g_pointer)->Format;
  495.  
  496.                  tracks=((HeaderChunk *)g_pointer)->Tracks;
  497.  
  498.                  printf("%s %d %s %d ", "This is a ",
  499.                   tracks," track SMF type ",format);
  500.  
  501.                  /* now deal with each new track chunk in turn  
  502.                     - at the moment the g_pointer is still at the 
  503.                     start of the buffer, ie at the start of the 
  504.                     HEADER chunk.... */
  505.  
  506.                  for (i=0;i<tracks;i++)
  507.  
  508.                       { 
  509.  
  510.                        g_previous_datasize=ReadChunkSize(g_pointer); /* previous chunk */
  511.  
  512.                        g_pointer+=g_previous_datasize+sizeof(ChunkInfo); /* skip to new chunk */
  513.   
  514.                        g_local_pointer=g_pointer; /* start of chunk data */
  515.        
  516.                        /* Must check that we've got a track chunk */
  517.                        
  518.                        identity=ReadChunkID(g_pointer);
  519.                        
  520.                        if(identity!=ID_TRACK) 
  521.                        
  522.                           {
  523.                                 
  524.                            printf(ERROR_MESSAGE_4);
  525.                         
  526.                            i--; /* adjust track counter or we 
  527.                                    will lose a real track */
  528.                           
  529.                           }
  530.                         
  531.                         else
  532.                         
  533.                           { g_current_datasize=ReadChunkSize(g_pointer);
  534.  
  535.                            g_local_pointer+=sizeof(ChunkInfo); /* start of data */
  536.  
  537.                            AnalyseTrackChunk(); /* read events */
  538.                           
  539.                           }
  540.    
  541.                        } /* end of for-loop */
  542.  
  543.                   if (g_unrecognized_byte) {printf(ERROR_MESSAGE_3);}
  544.  
  545.                        else { /* Display some results */
  546.                         
  547.                              printf(NO_ERROR_MESSAGE);
  548.                              
  549.                              printf("Meta event count.............. %d\n", 
  550.                                      g_meta_event_count);
  551.                          
  552.                              printf("Sysex event count............. %d\n",
  553.                                      g_sysex_event_count);
  554.                               
  555.                              printf("Note Off event count.......... %d\n",
  556.                                      g_note_off_event_count);
  557.                                   
  558.                              printf("Note On event count........... %d\n",
  559.                                      g_note_on_event_count);
  560.           
  561.                              printf("Poly AT event count........... %d\n",
  562.                                      g_pat_event_count);
  563.                              
  564.                              printf("Control change event count.... %d\n",
  565.                                      g_control_change_event_count); 
  566.                             
  567.                              printf("Program change event count.... %d\n", 
  568.                                      g_program_change_event_count);
  569.                              
  570.                              printf("Channel pressure event count.. %d\n", 
  571.                                      g_channel_pressure_event_count);
  572.          
  573.                              printf("Pitchbend event count......... %d\n",
  574.                                      g_pitchbend_event_count);
  575.  
  576.                             }
  577.  
  578.                    } /* end of Standard MIDI File analysis */
  579.                 
  580.                 } /* end of successful file load */
  581.  
  582. } /* END OF MAIN - AND ALSO THE 'LOGICAL END' OF THE PROGRAM */
  583.  
  584. /* ----------------------------------------------------------------------- */
  585.  
  586. BOOL LoadBuffer(TEXT *filename, UBYTE *buffer_p, LONG buffersize)
  587.  
  588. {
  589.  
  590. WORD c; LONG count=0; FILE *file_p;
  591.  
  592. BOOL exit_flag=FALSE, error_flag=FALSE;
  593.  
  594. if (file_p=fopen(filename, READ))
  595.  
  596.       {
  597.  
  598.        while(!exit_flag)
  599.   
  600.           {  
  601.     
  602.           c=getc(file_p); count++;
  603.       
  604.           if(c!=EOF) 
  605.           
  606.               {
  607.                 
  608.                if (count<=buffersize) {*(UBYTE *)buffer_p++=c;}
  609.                 
  610.                 else {error_flag=TRUE;}
  611.               
  612.                }
  613.  
  614.                else {exit_flag=TRUE;}
  615.  
  616.            }
  617.  
  618.        fclose(file_p); 
  619.  
  620.        } /* end of file open OK */
  621.     
  622.  else {error_flag=TRUE;}
  623.            
  624. return(error_flag);
  625.  
  626. }
  627.  
  628. /* ----------------------------------------------------------------------- */
  629.  
  630. void AnalyseTrackChunk(void)
  631.  
  632. {
  633.  
  634. /* This routine uses a global copy of the current chunk g_pointer. Why ? It's
  635.    so at the end of the analysis the real chunk pointer is still pointing
  636.    to the start of the chunk - in readiness for skipping to next chunk */
  637.  
  638. ULONG time;
  639.  
  640. UBYTE event_type;
  641.  
  642. /* This loop will examine track data. If it comes across an event
  643.    that cannot be understood... it quits that the analysis of the
  644.    current track chunk */ 
  645.  
  646. global_exit_flag=FALSE;
  647.  
  648. do
  649.  
  650.    {
  651.  
  652.    time=ReadVarLen(); event_type=*g_local_pointer;
  653.  
  654.    if (event_type<0x80)
  655.  
  656.          { 
  657.  
  658.           MidiEvent();
  659.  
  660.           }
  661.    
  662.     else {
  663.       
  664.          g_local_pointer++; /* skip identifers for these events */
  665.      
  666.          switch(event_type)
  667.         
  668.                 {
  669.                 
  670.                 case 0xF0: SysexEvent(); g_current_status=NULL; break;
  671.                       
  672.                 case 0xF7: SysexEvent(); g_current_status=NULL; break;
  673.                                 
  674.                 case 0xFF: MetaEvent();  g_current_status=NULL; break;
  675.                 
  676.                 default:   g_current_status=event_type&0xF0; MidiEvent();
  677.  
  678.                 }
  679.  
  680.          }
  681.  
  682.    if (g_local_pointer>=g_pointer+g_current_datasize+sizeof(ChunkInfo))
  683.  
  684.          {global_exit_flag=TRUE; }
  685.  
  686.    }while(!global_exit_flag);
  687.  
  688. }
  689.  
  690. /* ----------------------------------------------------------------------- */
  691.  
  692. void MetaEvent(void)
  693.  
  694. {
  695.  
  696. ULONG event_datasize, event_identifier;
  697.  
  698. event_identifier=*g_local_pointer; 
  699.  
  700. g_local_pointer++;
  701.  
  702. g_meta_event_count++; 
  703.  
  704. event_datasize=ReadVarLen();
  705.  
  706. g_local_pointer+=event_datasize;
  707.  
  708. }
  709.  
  710. /* ----------------------------------------------------------------------- */
  711.  
  712. void MidiEvent(void)
  713.  
  714. {
  715.  
  716. switch(g_current_status)
  717.  
  718.         {
  719.   
  720.   
  721.         case NOTE_OFF:          g_local_pointer+=NOTE_OFF_SIZE;
  722.                                 g_note_off_event_count++;
  723.                                 break;
  724.  
  725.         case NOTE_ON:           g_local_pointer+=NOTE_ON_SIZE;
  726.                                 g_note_on_event_count++;
  727.                                 break;
  728.  
  729.         case PAT:               g_local_pointer+=PAT_SIZE;
  730.                                 g_pat_event_count++;
  731.                                 break;
  732.  
  733.         case CONTROL_CHANGE:    g_local_pointer+=CONTROL_CHANGE_SIZE;
  734.                                 g_control_change_event_count++;
  735.                                 break;
  736.  
  737.         case PROGRAM_CHANGE:    g_local_pointer+=PROGRAM_CHANGE_SIZE;
  738.                                 g_program_change_event_count++;
  739.                                 break;
  740.  
  741.         case CHANNEL_PRESSURE:  g_local_pointer+=CHANNEL_PRESSURE_SIZE;
  742.                                 g_channel_pressure_event_count++;
  743.                                 break;
  744.  
  745.         case PITCHBEND:         g_local_pointer+=PITCHBEND_SIZE;
  746.                                 g_pitchbend_event_count++;
  747.                                 break;
  748.         
  749.         default: g_unrecognized_byte=TRUE; global_exit_flag=TRUE;
  750.         
  751.         }
  752.  
  753. }
  754.  
  755. /* ----------------------------------------------------------------------- */
  756.  
  757. void SysexEvent(void)
  758.  
  759. {
  760.  
  761. ULONG event_datasize;
  762.  
  763. event_datasize=ReadVarLen();
  764.  
  765. g_local_pointer+=event_datasize;
  766.  
  767. g_sysex_event_count++;
  768.  
  769. }
  770.  
  771. /* ----------------------------------------------------------------------- */
  772.  
  773. ULONG ReadVarLen(void)
  774.  
  775. {
  776.  
  777. ULONG value; UBYTE c;
  778.  
  779. value=(ULONG)*g_local_pointer++;
  780.  
  781. if(value&0x80)
  782.  
  783.    { 
  784.    
  785.    value&=0x7F;
  786.    
  787.    do {value=(value<<7)+((c=*g_local_pointer++)&0x7F);} while(c&0x80);
  788.  
  789.    }
  790.  
  791. return(value);
  792.  
  793. }
  794.  
  795. /* ----------------------------------------------------------------------- */
  796.  
  797. ULONG ReadChunkSize(UBYTE *RCS_pointer)
  798.  
  799. {
  800.  
  801. ULONG value; UBYTE i;
  802.  
  803. RCS_pointer+=4; value=*RCS_pointer++;
  804.  
  805. for (i=0;i<3;i++) { value=(value<<8)+(*RCS_pointer++);}
  806.  
  807. return(value);
  808.  
  809. }
  810. /* ----------------------------------------------------------------------- */
  811.  
  812. ULONG ReadChunkID(UBYTE *RCID_pointer)
  813.  
  814. {
  815.  
  816. ULONG value; UBYTE i;
  817.  
  818. value=*RCID_pointer++;
  819.  
  820. for (i=0;i<3;i++) { value=(value<<8)+(*RCID_pointer++);}
  821.  
  822. return(value);
  823.  
  824. }
  825.  
  826. /* ======================================================================= */
  827.  
  828.  
  829.  
  830.  
  831.  
  832.  
  833.  
  834.  
  835.  
  836.